package org.discala.samples.complex.chef

import org.discala.samples.complex.domain.Ingredient

/**
 * @author Chris Turner
 */
private[chef] abstract class BasicVessel extends Vessel {

  private[this] var contents = List[Ingredient]()

  def <<(ingredient: Ingredient) = contents = ingredient :: contents

  def describeContents() = {
    vesselProduct + listContents(contents)
  }

  protected def vesselProduct(): String

  def describeAroma(): String = {
    val items = strongest { _.aromaStrength }
    if ( items.isEmpty ) return "no aroma at all"

    items.head.aromaStrength match {
      case high if ( high > 0.7f ) => "a pungent whiff of " + listContents(items)
      case medium if ( medium > 0.4f ) => "a pleasent aroma of " + listContents(items)
      case _ => "a faint breath of " + listContents(items)
    }
  }

  def describeTaste(): String = {
    val items = strongest { _.tasteStrength }
    if ( items.isEmpty ) return "no taste at all"

    items.head.tasteStrength match {
      case high if ( high > 0.7f ) => "an overpowering taste of " + listContents(items)
      case medium if ( medium > 0.4f ) => "a lovely taste of " + listContents(items)
      case _ => "a bland taste of " + listContents(items)
    }
  }

  private[this] def listContents(items: List[Ingredient]): String = {
    items.length match {
      case 0 => "thin air"
      case 1 => items.head.name
      case 2 => items.head.name + " and " + listContents(items.tail)
      case _ => items.head.name + ", " + listContents(items.tail)
    }
  }

  private[this] val strongest = findStrongest(contents, List(), 0.0f)(_)

  private[this] def findStrongest(items: List[Ingredient],
                                  strongest: List[Ingredient],
                                  max: Float)(strength: (Ingredient) => Float): List[Ingredient] = {
    if ( items.isEmpty ) return strongest
    items.head match {
      case higher if ( strength(higher) > max ) =>
        findStrongest(items.tail, List(higher), strength(higher))(strength)
      case same if ( strength(same) == max ) =>
        findStrongest(items.tail, same :: strongest, max)(strength)
      case _ =>
        findStrongest(items.tail, strongest, max)(strength)
    }
  }
}